﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Reflection;
using Kevin.Framework.Cache;
using Kevin.Framework.Core;
using System.Windows.Forms;
using System.ComponentModel.Design.Serialization;
using Kevin.Framework.Validation;
using System.Collections;
using System.Text.RegularExpressions;
/*-----------------------------------------------------------------------
 * 文件名：DataBind.cs
 * 作者  ：席吉波  2012-11-13
 * 用途  ：完成Winform界面数据实体与控件之间的双向绑定、校验功能
 *----------------------------------------------------------------------- 
 */
namespace Kevin.Controls
{
    
    public partial class DataBind : Component
    {
        DataBindItemCollection _dataBindItems;
        public DataBind()
        {
            InitializeComponent();
        }

        public DataBind(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            _dataBindItems = new DataBindItemCollection();
        }

        #region Properties

       
        [Description("控件与实体属性绑定配置")]
        [Editor(typeof(DataBindItemCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
        [TypeConverter(typeof(DataBindItemCollectionCoverter))]
        public DataBindItemCollection DataBindItems
        {
            get { return _dataBindItems; }
            set { _dataBindItems = value; }
        }
        
        private object _data;
        /// <summary>
        /// 数据
        /// </summary>
        [Description("与本控件绑定的实体")]  
        public object Data
        {
            get 
            {
                OldData = _data;
                MappingControlsToData(_data, DataBindItems);                
                return _data; 
            }
            set 
            { 
                _data = value;
                this.Data2Controls(_data, this.DataBindItems);
                OldData = _data;
            }
        }
        /// <summary>
        /// 过期的数据
        /// </summary>
        private object OldData
        {
            get 
            {
                object result;
                DataBindDataCache.Instance.TryGetValue(_data.GetType().ToString(), out result);
                return result;
            }
            set 
            {
                if (_data == null) return;
                DataBindDataCache.Instance.Add(_data.GetType().ToString(), value);
            }
        }

        bool _getChangedValueOnly = true;
        /// <summary>
        /// 是否获取只改变过的值,默认：是
        /// </summary>
        public bool GetChangedValueOnly
        {
            get { return _getChangedValueOnly; }
            set { _getChangedValueOnly = value; }
        }

        /// <summary>
        /// 控件的值是否已改变
        /// </summary>
        public bool IsControlsValueChanged
        {
            get;
            set;
        }

        bool _validateSuccess=true;
        /// <summary>
        /// 控件的值 是否校验成功
        /// </summary>
        public bool ValidateSuccess
        {
            get { return _validateSuccess; }
        }

        #endregion

        #region Data to Controls

        private void Data2Controls(object sourceData, DataBindItemCollection items)
        {
            if (sourceData == null || items == null)
            {
                return;
            }

            foreach (DataBindItem item in items)
            {
                if ((item.BindDirection & BindDirection.DataToControl) != BindDirection.None)
                {
                    object subData = TypePropertiesCacheQueue.Instance.GetObjectPropertyValue(sourceData,
                                    GetObjectPropertyName(sourceData, item.PropertyName));

                    MapOneData2Control(TypePropertiesCacheQueue.Instance.GetPropertyInfo(sourceData.GetType(),
                                    GetObjectPropertyName(sourceData, item.PropertyName)), subData, item);
                }
            }
        }

        private void MapOneData2Control(PropertyInfo dataPropertyInfo, object data, DataBindItem item)
        {
            Control targetControl = item.TargetControl;

            this.TargetControlInputValidate(targetControl, dataPropertyInfo,item);

            if (data == null)
            {
                return;
            }
            

            PropertyInfo piDest = TypePropertiesCacheQueue.Instance.GetPropertyInfo(targetControl.GetType(),
                        GetObjectPropertyName(targetControl, item.ControlPropertyName));
            if (piDest.CanWrite)
            {
                if (SetControlsAttributesByDataType(dataPropertyInfo, data, targetControl, item) == false)
                {
                    object targetValue = FormatDataProperty(data, piDest, item);

                    piDest.SetValue(targetControl, targetValue, null);

                }
            }
        }
        /// <summary>
        /// 给控件添加录入校验事件
        /// </summary>
        /// <param name="targetControl">目标控件</param>
        /// <param name="dataPropertyInfo">需绑定的值属性</param>
        private void TargetControlInputValidate(Control targetControl, PropertyInfo dataPropertyInfo, DataBindItem item)
        {
            //这里给控件绑定事件，如果是文本框且只能输入数字时进行拦截
            if (targetControl is TextBox)
            {
                List<ValidateDefine> regs = null;
                //基础校验 必须执行
                if (IsInt(dataPropertyInfo.PropertyType))
                {
                    //List<ValidateDefine> regs = new List<ValidateDefine>();
                    if (!controlsRegs.TryGetValue(targetControl.Name, out regs))
                    {
                        regs = new List<ValidateDefine>();
                        regs.Add(new ValidateDefine() { Regex = "^[0-9]*$", ErrorMsg = "请输入数字" });
                        controlsRegs.Add(targetControl.Name, regs);
                    }
                    targetControl.KeyPress += new KeyPressEventHandler(targetControl_LostFocus);
                }
                else if (IsDecimals(dataPropertyInfo.PropertyType))
                {
                    
                    if (!controlsRegs.TryGetValue(targetControl.Name, out regs))
                    {
                        regs =new List<ValidateDefine>();
                        regs.Add(new ValidateDefine() { Regex = @"^(-?\d+)(\.\d+)*$", ErrorMsg = "请输入数字" });
                        controlsRegs.Add(targetControl.Name, regs);
                    }
                    //targetControl.KeyPress += new KeyPressEventHandler(targetControl_KeyPress);
                    targetControl.LostFocus += new EventHandler(targetControl_LostFocus);
                }
            }
            //自定义的验证
            if (item.DoCustomsRegexValidate && !string.IsNullOrEmpty(item.CustomsRegexValidate.Trim()))
            {
                List<ValidateDefine> regs = new List<ValidateDefine>();
                if (!controlsRegs.TryGetValue(targetControl.Name, out regs))
                {
                    regs.Add(new ValidateDefine() { Regex = item.CustomsRegexValidate.Trim(), ErrorMsg =string.IsNullOrEmpty(item.CustomsErrorMsg)?"请输入正确的数据": item.CustomsErrorMsg });
                    controlsRegs.Add(targetControl.Name, regs);
                }
                
            }
        }
        Dictionary<string, List<ValidateDefine>> controlsRegs = new Dictionary<string, List<ValidateDefine>>();
        void targetControl_LostFocus(object sender, EventArgs e)
        {
            TextBox txt = sender as TextBox;
            string txtValue = txt.Text.Trim();
            if (string.IsNullOrEmpty(txtValue))
                return;

            List<ValidateDefine> regex = new List<ValidateDefine>(); 
            if (controlsRegs.TryGetValue(txt.Name, out regex))
            {
                bool result = true;
                foreach (ValidateDefine strRegex in regex)
                {


                    Regex reg = new Regex(strRegex.Regex);

                    if (!reg.IsMatch(txtValue))
                    {
                        _Validateprovider.SetError(txt, "请录入正确的数据");
                        txt.Focus();
                    }
                    else
                    {
                        _Validateprovider.Clear();
                    }
                }
            }
        }
        
       
        private object FormatDataProperty(object sourceData, PropertyInfo piDest, DataBindItem item)
        {
            object targetValue = null;
            targetValue = DataConverter.ChangeType(sourceData, piDest.PropertyType);

            if (piDest.PropertyType == typeof(string))
            {
                Type srcDataType = sourceData.GetType();
                //object val = ((DefaultValueAttribute)TypeDescriptor.GetProperties(piDest)[item.DataPropertyName].Attributes[typeof(DefaultValueAttribute)]).Value;

                if (srcDataType.IsValueType)
                {
                    object defaultValue = TypeCreator.CreateInstance(srcDataType);
                    if (sourceData.Equals(defaultValue))
                    {
                        targetValue = string.Empty;
                    }
                }
                //除掉else，只要是设置了格式化且不为默认数据，就应该格式化数据。 
                if (string.IsNullOrEmpty(item.DataFormat) == false && !sourceData.Equals(TypeCreator.CreateInstance(srcDataType)))
                    targetValue = string.Format(item.DataFormat, sourceData);
            }

            return targetValue;
        }

        private bool SetControlsAttributesByDataType(PropertyInfo dataPropertyInfo, object data, Control targetControl, DataBindItem item)
        {
            bool binded = false;
            if (targetControl is ListControl && data.ToString().Length <= 0) return binded;//防止对ListControl进行空值绑定，导致错误。
            if (dataPropertyInfo.PropertyType.IsEnum)
            {
                if (targetControl is ListControl)
                {
                    //BindEnumToListControl(dataPropertyInfo.PropertyType, data, (ListControl)targetControl, item);
                    binded = true;
                }
                //else if (targetControl is TextBox ITextControl)
                //{
                //    //BindEnumToTextControl(data, (ITextControl)control, item);
                //    binded = true;
                //}
            }

            return binded;
        }
        /// <summary>
        /// 将枚举数据绑定至列表型控件
        /// </summary>
        /// <param name="enumType">枚举类型</param>
        /// <param name="data">当前数据</param>
        /// <param name="ddl">下拉控件</param>
        /// <param name="item">绑定数据</param>
        private void BindEnumToListControl(System.Type enumType, object data, ListControl lControl, DataBindItem item)
        {
            if (item.EnumAutoBinding)
            {
                //Kevin.Framework.Core.EnumDescList
                EnumDescList listEnum = EnumDescAttribute.GetDescriptionList(enumType);

                List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

                foreach (EnumItemDescription enumItem in listEnum)
                {
                    KeyValuePair<int, string> kp = new KeyValuePair<int, string>(enumItem.EnumValue,
                        string.IsNullOrEmpty(enumItem.Description) ? enumItem.Name : enumItem.Description);

                    list.Add(kp);
                }

                lControl.ValueMember = "Key";
                lControl.DisplayMember = "Value";

                lControl.DataSource = BuildEnumList(enumType, item.EnumUsage);

                
            }

            lControl.SelectedValue = ((int)data).ToString();
        }

        private IList BuildEnumList(System.Type enumType, EnumUsageTypes enumUsage)
        {
            IList result = null;

            EnumDescList listEnum = EnumDescAttribute.GetDescriptionList(enumType);

            if (enumUsage == EnumUsageTypes.UseEnumValue)
            {
                List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

                foreach (EnumItemDescription enumItem in listEnum)
                {
                    KeyValuePair<int, string> kp = new KeyValuePair<int, string>(enumItem.EnumValue,
                        string.IsNullOrEmpty(enumItem.Description) ? enumItem.Name : enumItem.Description);

                    list.Add(kp);
                }

                result = list;
            }
            else
            {
                List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();

                foreach(EnumItemDescription enumItem in listEnum)
                {
                    KeyValuePair<string, string> kp = new KeyValuePair<string, string>(enumItem.Name,
                        string.IsNullOrEmpty(enumItem.Description) ? enumItem.Name : enumItem.Description);

                    list.Add(kp);
                }

                result = list;
            }

            return result;
        }
        #endregion

        #region Controls to Data

        protected void MappingControlsToData(object sourceData, DataBindItemCollection items)
        {
            this._validateSuccess = true;

            if (sourceData != null && items != null)
            {
                //this.IsControlsValueChanged = false;

                foreach (DataBindItem item in items)
                {
                    if ((item.BindDirection & BindDirection.ControlToDdata) != BindDirection.None)
                    {
                        //if (item.SubBind.Count > 0)
                        //{
                        //    object subData = TypePropertiesCacheQueue.Instance.GetObjectPropertyValue(sourceData,
                        //        GetObjectPropertyName(sourceData, item.DataPropertyName));
                        //}
                        //else
                        //{
                            MappingOneControlToOneData(sourceData, item);
                        //}
                    }
                }
                
            }
            if (this._validateSuccess)
            {
                _Validateprovider.Clear();
            }
        }

        private void MappingOneControlToOneData(object data, DataBindItem item)
        {
            Control targetControl = item.TargetControl;// FindControlByPath(this, item.ControlID);

            //ExceptionHelper.FalseThrow(targetControl != null, "不能找到ID为{0}的控件", targetControl.ID);

            object sourceData = TypePropertiesCacheQueue.Instance.GetObjectPropertyValue(targetControl,
                GetObjectPropertyName(targetControl, item.ControlPropertyName));

            PropertyInfo piDest = TypePropertiesCacheQueue.Instance.GetPropertyInfo(data.GetType(),
                GetObjectPropertyName(data, item.PropertyName));

            if (DoValidate(item, sourceData, piDest))
            {

                if (piDest.CanWrite)
                {
                    object targetValue = UnformatControlProperty(sourceData, piDest, item);
                    if (this.GetChangedValueOnly)
                    {
                        object oldObj = OldData;// ViewState["data"];//页面上已经绑定的数据
                        if (oldObj != null)
                        {
                            object oldData = TypePropertiesCacheQueue.Instance.GetObjectPropertyValue(oldObj
                                        , GetObjectPropertyName(oldObj, item.PropertyName));
                            if (oldData == null )
                            {
                                if (!string.IsNullOrEmpty(targetValue.ToString()))
                                {
                                    piDest.SetValue(data, targetValue, null);
                                    this.IsControlsValueChanged = true;
                                }
                            }
                            else
                            {
                                if (!oldData.Equals(targetValue))//如果页面上控件的值发生改变
                                {
                                    piDest.SetValue(data, targetValue, null);
                                    this.IsControlsValueChanged = true;
                                }
                            }
                        }
                    }
                    else
                    {
                        piDest.SetValue(data, targetValue, null);
                    }
                }
            }
        }

        private object UnformatControlProperty(object sourceData, PropertyInfo piDest, DataBindItem item)
        {
            object targetValue = sourceData;

            if (ControlDataIsNull(sourceData))
            {
                if (piDest.PropertyType.IsValueType)
                    targetValue = TypeCreator.CreateInstance(piDest.PropertyType);
            }
            else
            {
                if (IsNumericType(piDest.PropertyType))
                {
                    if (sourceData.GetType() == typeof(string))
                        sourceData = ((string)sourceData).Replace(",", string.Empty);
                }
                targetValue = DataConverter.ChangeType(sourceData, piDest.PropertyType);
            }
            return targetValue;
        }

        private bool IsNumericType(Type type)
        {
            bool result = false;

            result = IsInt(type) | IsDecimals(type);

            return result;
        }

        private bool IsInt(Type type)
        {
            bool result = false;
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:                
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    result = true;
                    break;
            }
            return result;
        }

        private bool IsDecimals(Type type)
        {
            bool result = false;
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.Decimal:
                case TypeCode.Double:                
                case TypeCode.SByte:
                case TypeCode.Single:
                    result = true;
                    break;
            }
            return result;
        }


        private bool ControlDataIsNull(object sourceData)
        {
            bool result = false;

            if (sourceData == null)
                result = true;
            else
                if (sourceData is string)
                    result = (string)sourceData == string.Empty;

            return result;
        }

        #endregion

        #region Do Validate

        ErrorProvider _Validateprovider = new ErrorProvider();
        
        /// <summary>
        /// 执行校验
        /// </summary>
        /// <param name="item"></param>
        /// <param name="data"></param>
        /// <param name="pDesc"></param>
        private bool DoValidate(DataBindItem item,object data,PropertyInfo pDesc)
        {
            
            ValidatorCompositionAttribute Composition = AttributeHelper.GetCustomAttribute<ValidatorCompositionAttribute>(data.GetType());
            if (Composition != null)
            {
 
            }
            Kevin.Framework.Validation.ValidatorAttribute[] vObjects = (Kevin.Framework.Validation.ValidatorAttribute[])pDesc.GetCustomAttributes(typeof(Kevin.Framework.Validation.ValidatorAttribute), true);
                //AttributeHelper.GetCustomAttribute<Kevin.Framework.Validation.ValidatorAttribute>(data.GetType());
            if (vObjects == null || vObjects.Length==0)
                return true;
            foreach (ValidatorAttribute validator in vObjects)
            {                
                switch(validator.GetType().ToString())
                {
                    case "Kevin.Framework.Validation.NotNullValidatorAttribute":
                        if (string.IsNullOrEmpty(item.TargetControl.Text.Trim()))
                        {
                            this._validateSuccess = false;                            
                            _Validateprovider.SetError(item.TargetControl, validator.MessageTemplate);
                            item.TargetControl.Focus();
                            return false;
                        }
                        break;
                    
                    default:
                        break;
                }
            }
            if (IsNumericType(pDesc.PropertyType)) {
                try
                {
                    DataConverter.ChangeType(item.TargetControl.Text, pDesc.PropertyType);
                }
                catch {
                    this._validateSuccess = false;
                    _Validateprovider.SetError(item.TargetControl, "请输入数字！");
                    return false;
                }
            }
            if (pDesc.PropertyType == typeof(DateTime))
            {
                if (!string.IsNullOrEmpty(item.TargetControl.Text))
                {
                       DateTime dt;
                       if (!DateTime.TryParse(item.TargetControl.Text, out dt))
                       {
                           this._validateSuccess = false;
                           _Validateprovider.SetError(item.TargetControl, "请输入正确的日期！");
                           return false;
                       }
                    
                }
            }
            return true;
        }

        #endregion

        #region Helper

        private string GetObjectPropertyName(object data, string originalName)
        {
            if (data == null)
                return "";

            string result = originalName;

            if (string.IsNullOrEmpty(originalName) == true)
            {
                DefaultPropertyAttribute attr = AttributeHelper.GetCustomAttribute<DefaultPropertyAttribute>(data.GetType());

                if (attr != null)
                    result = attr.Name;
            }

            return result;
        }

        #endregion
    }

   
}
